/*
 * @Description:
 * @Author: Haor
 * @Date: 2022-03-31 20:37:58
 * @LastEditTime: 2022-05-09 13:27:13
 * @LastEditors: Haor
 */
import { Injectable } from '@nestjs/common'
import { HttpResTemplate, HttpResult } from "@libs/http.result";
import { UserService } from '../user/user.service'
import { JwtService } from '@nestjs/jwt'
import { AuthLoginDto } from './dto/auth-login.dto'
import { ParamsException, BusinessException, ServerException } from "@exceptions";
import * as svgCaptcha from 'svg-captcha'
import { CacheService } from "@cache";
import { User } from "@entities/user.entity";
import { CacheKeyConstant } from '@/common/constants/cache.key.constant'
import { MenuService } from "@/modules/menu/menu.service";
import { tryCatch } from "rxjs/internal-compatibility";
import { MenuEntity } from "@entities/menu.entity";
import { listDeleteBy } from "@utils/data";
import { CatchError } from "@decorators/cache-error.decorator";

interface ValidataUserParams {
	userName: string
	password: string | number
}
@Injectable()
export class AuthService {
	constructor(
	    private cacheService: CacheService,
	    private userService: UserService,
        private menuService: MenuService,
        private jwtService: JwtService) {
    }

	/**
	 * 用户登录
	 * @param authLogin
	 */
    @CatchError('登录')
	async login(authLoginDto: AuthLoginDto): Promise<HttpResTemplate> {
        // 通过用户名查找用户是否存在
        const user: User = await this.userService.getUserByName(authLoginDto.userName)
        const errorMessage = '用户名或密码有误'
        if (!user) {
            throw new BusinessException(errorMessage) //自定义异常
        }

        // 通过用户名称取出用户密码， 备注（忽略加密与数据密文对比步骤）
        const userPassword = await this.userService.getUserPassword({
            userName: authLoginDto.userName
        })

        // 判断密码错误
        if (authLoginDto.password !== userPassword) {
            throw new BusinessException(errorMessage)
        }

        await this.saveLoginInfo(user) // 存储登录信息
        const accessToken = this.genToken({
            userName: user.userName,
            userId: user.id,
            jobId: user.jobId,
            nickName: user.nickName,
            email: user.email,
            createTime: Date.now() // token生成时间
        })

        // redis存储用户Token
        await this.cacheService.set(`${CacheKeyConstant.AUTH_USER_TOKEN}${user.id}`, accessToken)

        // 生成token
        return HttpResult.success({
            accessToken: accessToken,
            info: user
        })
	}


    /**
     * 退出登录
     * @param userId
     */
    @CatchError('退出登录')
	async logout(userId: number): Promise<HttpResTemplate> {
        await this.deleteCacheUserInfo(userId) // 删除缓存信息
        return HttpResult.success()
    }

	// 生成 token
	genToken(payload): string {
		const tokenPrefix = `Bearer `
		return `${this.jwtService.sign(payload, {
		    algorithm: 'HS256'
        })}`
	}

    /**
     * 获取验证码
     */
    getCaptcha() {
        const captcha = svgCaptcha.createMathExpr({
            color: false,
            background: '#cc9966',
            fontSize: 50,
            charPreset: '1+1=2?',
            mathMin: 1,
            mathMax: 9,
            mathOperator: '+'
        })
        console.log(captcha.data)
        return captcha.data

        // const random = randomNumber(4)
        // const captcha = new captchapng(80, 30, random)
        // captcha.color(0, 0, 0, 0);
        // captcha.color(80, 80, 80, 255);
        // const base64 = captcha.getBase64();
        // return HttpResult.success(base64)
    }

	// 刷新 token
	refreshToken(id: number): string {
		return ''
	}

    /**
     * 校验token
     * @param {number} userId
     * @param {string} token
     * @reutrn {Promise<boolean>}
     */
	async verifyToken(userId: number, token: string): Promise<boolean>  {
        try{
            const tokenCheck = this.jwtService.verify(token)
            const cacheToken: string = await this.cacheService.get(`${CacheKeyConstant.AUTH_USER_TOKEN}${userId}`)
            console.log('tokenCheck', tokenCheck)
            console.log('缓存中的token->>>>', cacheToken)
            return tokenCheck && cacheToken && cacheToken === token;
        } catch(e) {
            throw new ServerException(e)
        }
	}

    /**
     * 验证用户
     * @param {string} userName
     * @param {string} password
     */
    @CatchError('验证用户')
	async validateUser({ userName, password }: ValidataUserParams): Promise<User> {
        const user: User = await this.userService.getUserByNameAndPassword(userName, "" + password)
		if (!user) {
            throw new BusinessException('用户不存在')
        }
		return user
	}

    /**
     * 获取登录用户信息
     * @param {number} userId
     * @return {Promise<HttpResTemplate>} res
     */
    async getLoginUser(userId:number): Promise<HttpResTemplate> {
        const cacheUser: User = await this.cacheService.get(`${CacheKeyConstant.SYS_LOGIN_USER}${userId}`) // 从redis中取出登录用户
        return HttpResult.success(cacheUser)
    }

    /**
     * 生成菜单
     * @param userId
     */
    @CatchError('生成菜单')
    async buildMenu(userId: number): Promise<HttpResTemplate> {
        const menuList: Array<MenuEntity> = await this.menuService.buildUserMenu(userId)
        return HttpResult.success(menuList)
    }

    /**
     * 存储登录信息
     * @private
     * @param {User} user 用户
     */
    @CatchError('存储登录信息')
    private async saveLoginInfo(user: User) {
        await this.cacheService.set(`${CacheKeyConstant.SYS_LOGIN_USER}${+user.id}`, user) //存储用户登录信息
        let ONLINE_USERS: Array<number> = await this.cacheService.get(CacheKeyConstant.SYS_ONLINE_USERS)
        if (!Array.isArray(ONLINE_USERS)) {
            ONLINE_USERS = []
        }

        if (!ONLINE_USERS.includes(+user.id)) {
            ONLINE_USERS.push(+user.id)
        }
        // redis存储登录用户
        await this.cacheService.set(`${CacheKeyConstant.SYS_ONLINE_USERS}`, ONLINE_USERS)
    }

    /**
     * 删除登录信息
     * @param userId
     * @private
     */
    @CatchError('删除登录信息')
    private async deleteCacheUserInfo(userId: number) {
        await this.cacheService.del(`${CacheKeyConstant.SYS_LOGIN_USER}${userId}`) //删除用户登录信息
        await this.cacheService.del(`${CacheKeyConstant.AUTH_USER_TOKEN}${userId}`) // 删除token
        let onlineUsers = await this.cacheService.get(CacheKeyConstant.SYS_ONLINE_USERS)
        if (!onlineUsers) {
            await this.cacheService.set(CacheKeyConstant.SYS_ONLINE_USERS, [])
            return Promise.resolve()
        }

        // 如果用户id存在，则删除
        if (Array.isArray(onlineUsers) && onlineUsers.includes(userId)) {
            listDeleteBy(onlineUsers, uId => +uId === +userId, true)
            await this.cacheService.set(CacheKeyConstant.SYS_ONLINE_USERS, onlineUsers)
        }
        return Promise.resolve()
    }
}
